﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using ZedGraph;
using SoftwareConsulting.BI11.Studio.Core.Project;
using SoftwareConsulting.BI11.Studio.Core.Project.SituationsHierarchy;
using SoftwareConsulting.BI11.Studio.Core.Project.Metadata;
using SoftwareConsulting.BI11.Studio.BasicEntities;
using SoftwareConsulting.Interfaces.BasicEntities;
using SoftwareConsulting.BI11.Studio.InternalServices;
using SoftwareConsulting.BI11.Studio.UI.Properties;
using SoftwareConsulting.BI11.Studio.UI.ProjectExplorer.Extensions;

namespace SoftwareConsulting.BI11.Studio.UI.ProjectExplorer
{
    public partial class RapidAnalysisForm : RestorableForm
    {
        private ProjectExplorerForm _projectExplorerForm;//для связи с формой обозревателя проекта
        private IDateAndFactCorrelations _curViewingObject;//просматриваемый объект
        private RapidAnalysisInfo _curRapidAnalysisInfo;//ядро быстрого анализа                
        private bool _programCheckBoxesUpdateFlag;//флаг программного обновления чекбоксов        
        private bool _programValidatedFlag;//флаг программной отмены проверки валидности введенного в ячейку значения
        private bool _noFixedInds; //флаг того, что пока еще не имеем отмеченных фиксир.измерений
        private int _firstValueableX; //X-координата первой значащей ячейки в строке
        private int _firstValueableY; //Y-координата первой значащей ячейки в строке
        //bbb private bool _valueableRows; //флаг того, что быстрый анализ реально подготовлен (есть выбранные строки и есть выбранные столбцы)        
        private bool _closing; //флаг состояния закрытия формы

        /// <summary>
        /// Возвращает текущий проект
        /// </summary>
        private ProjectDescriptor ProjectLink
        {
            get
            {
                return _projectExplorerForm.ProjectLink;
            }
        }

        /// <summary>
        /// Возвращает просматриваемый объект
        /// </summary>
        public IDateAndFactCorrelations CurrentViewingObject
        {
            get
            {
                return _curViewingObject;
            }
        }

        /// <summary>
        /// Возвращает дочерние узлы для данного дерева
        /// </summary>
        /// <param name="tree"></param>       
        /// <returns></returns>        
        private TreeNode[] GetChildNodesRecursively(INamedHierarchy<INamed> tree)
        {
            int c = tree.ItemsCount;
            TreeNode[] nodes = new TreeNode[c];            
            for (int i = 0; i < c; i++)
            {
                INamedHierarchy<INamed> item = tree.GetItemByPosition(i) as INamedHierarchy<INamed>;
                TreeNode newNode = new TreeNode(item.Name, GetChildNodesRecursively(item));
                newNode.Tag = item.Tag;
                nodes[i] = newNode;
            }
            return nodes;
        }

        /// <summary>
        /// Заполняет иерархию отмеченных элементов измерения в указанной позиции
        /// </summary>
        /// <param name="position"></param>
        private void ReadFrameHierarchy(TreeNodeCollection nodes, RapidAnalysisDimensionPosition position, INamedHierarchy<INamed> hierarchy)
        {
            int c = nodes.Count;
            for (int i = 0; i < c; i++)            
            {
                TreeNode node = nodes[i];
                bool lookNode = node.Checked;
                object nodeTag = node.Tag;
                string nodeText = node.Text;
                bool lookChilds = true;
                INamedHierarchy<INamed> newitem = new NamedHierarchy<INamed>(hierarchy);
                if (nodeTag is RapidAnalysisDimensionPosition)
                {                    
                    //это позиция быстрого анализа                                        
                    position = (RapidAnalysisDimensionPosition)nodeTag;
                    if (position == RapidAnalysisDimensionPosition.Fixed)
                    {
                        lookChilds = false;
                    }
                    newitem.SetName(nodeText);
                    newitem.Tag = node.Tag;
                    hierarchy.Add(newitem);
                }
                if (nodeTag is IDimension)
                {
                    //это измерение
                    lookChilds = lookNode;
                    newitem.SetName(nodeText);
                    newitem.Tag = node.Tag;
                    hierarchy.Add(newitem);
                }
                if (((nodeTag is int) || (nodeTag is DateTime) || (nodeTag is DatePeriod)) && lookNode)
                {
                    newitem.SetName(nodeText);
                    newitem.Tag = node.Tag;
                    hierarchy.Add(newitem);
                    lookChilds = true;
                }                
                TreeNodeCollection childNodes = node.Nodes;
                if (lookChilds && (childNodes.Count > 0))
                {
                    ReadFrameHierarchy(childNodes, position, newitem);
                }                                
            }
        }

        /// <summary>
        /// Заполняет рамку таблицы по измерению
        /// </summary>
        /// <param name="hierarchy"></param>
        /// <param name="startColumn"></param>
        /// <param name="currentRow"></param>
        private void FillFrameRecursively(INamedHierarchy<INamed> hierarchy, int currentColumn, int currentRow, int maxDeep, bool isColumnsDimension, IDimension dimension)
        {
            bool noShift = false;
            int newstart = -1;
            if (isColumnsDimension)
            {
                newstart = currentColumn;
            }
            else
            {
                newstart = currentRow;
            }
            int c = hierarchy.ItemsCount;
            System.Windows.Forms.Label headerCell = null;            
            bool notDimension = !(hierarchy.Tag is IDimension);
            if (notDimension)
            {
                headerCell = new System.Windows.Forms.Label();
                headerCell.AutoSize = true;                
                headerCell.BorderStyle = BorderStyle.FixedSingle;
                headerCell.BackColor = SystemColors.Control;
                headerCell.ForeColor = SystemColors.ControlText;
                headerCell.TextAlign = ContentAlignment.MiddleCenter;
                headerCell.Margin = System.Windows.Forms.Padding.Empty;
                headerCell.Text = hierarchy.Name;                
                RapidAnalysisDimensionPosition pos = isColumnsDimension ? RapidAnalysisDimensionPosition.Columns : RapidAnalysisDimensionPosition.Rows;
                headerCell.Tag = new RapidAnalysisCaptionCell(pos, new DimensionItem(dimension.Label, hierarchy.Tag));
                headerCell.Dock = DockStyle.Fill;
                ValuesArea.Controls.Add(headerCell, currentColumn, currentRow);
                if (isColumnsDimension)
                {
                    ValuesArea.SetColumnSpan(headerCell, hierarchy.LeafsCount);
                }
                else
                {
                    ValuesArea.SetRowSpan(headerCell, hierarchy.LeafsCount);
                }                                
            }
            else
            {
                noShift = true;
            }
            //int newmaxDeep = h.DeepestLevel;
            int shifted = noShift ? (isColumnsDimension ? currentRow : currentColumn) : (isColumnsDimension ? currentRow + 1 : currentColumn + 1);
            for (int i = 0; i < c; i++)
            {
                INamedHierarchy<INamed> item = hierarchy.GetItemByPosition(i) as INamedHierarchy<INamed>;
                FillFrameRecursively(item, isColumnsDimension ? newstart : shifted, isColumnsDimension ? shifted : newstart, /*new*/maxDeep, isColumnsDimension, dimension);
                newstart += item.LeafsCount;
            }
            if ((c == 0) && (notDimension))            
            {
                if (isColumnsDimension)
                {
                    ValuesArea.SetRowSpan(headerCell, maxDeep-currentRow/* + 1*/);
                }
                else
                {
                    ValuesArea.SetColumnSpan(headerCell, maxDeep-currentColumn/* + 1*/);
                }
            }
        }

        /// <summary>
        /// Строит таблицу быстрого анализа
        /// </summary>        
        private void UpdateValuesFrames(TreeNodeCollection nodes)
        {        
            //проходим первый раз по дереву элементов, чтобы составить
            //иерархию отмеченных элементов в измерения по столбцам
            //и по строкам            
            INamedHierarchy<INamed> h = new NamedHierarchy<INamed>();
            ReadFrameHierarchy(nodes, RapidAnalysisDimensionPosition.Columns, h);
            //вычисляем основные размеры таблицы
            int CornerHeight = -1;
            int CornerWidth = -1;
            int RowsCount = -1;
            int ColumnsCount = -1;
            int c = h.ItemsCount;            
            for (int i = 0; i < c; i++)
            {
                INamedHierarchy<INamed> item = h.GetItemByPosition(i) as INamedHierarchy<INamed>;
                if (item.Tag.Equals(RapidAnalysisDimensionPosition.Columns))
                {
                    CornerHeight = item.DeepestLevel - 1;
                }
                if (item.Tag.Equals(RapidAnalysisDimensionPosition.Rows))
                {
                    CornerWidth = item.DeepestLevel - 1;                  
                }
            }            
            ValuesArea.Controls.Clear();            
            if ((CornerWidth == 0) && (CornerHeight == 0))
            {
                //ничего не отмечено
                ValuesArea.ColumnCount = 0;
                ValuesArea.RowCount = 0;
                return;
            }
            for (int i = 0; i < c; i++)
            {
                INamedHierarchy<INamed> item = h.GetItemByPosition(i) as INamedHierarchy<INamed>;
                if (item.Tag.Equals(RapidAnalysisDimensionPosition.Columns))
                {
                    ColumnsCount = item.LeafsCount /*- 1*/;
                    if (CornerHeight != 0)
                    {
                        ColumnsCount += CornerWidth;
                    }
                }
                if (item.Tag.Equals(RapidAnalysisDimensionPosition.Rows))
                {                                      
                    RowsCount = item.LeafsCount /*- 1*/;
                    if ((CornerHeight != 0)&&(CornerWidth!=0))                    
                    {
                        RowsCount += CornerHeight;
                    }
                    else
                    {
                        ColumnsCount = CornerWidth;
                    }
                }
            }
            /// **************
            /// строим таблицу
            /// **************                       
            ValuesArea.ColumnCount = ColumnsCount;
            ValuesArea.RowCount = RowsCount;            
            //делаем уголок
            if ((CornerHeight != 0) && (CornerWidth != 0))
            {
                Panel cornerCell = new Panel();
                cornerCell.BackColor = SystemColors.ControlDark;
                cornerCell.BorderStyle = BorderStyle.None;                
                cornerCell.Dock = DockStyle.Fill;
                cornerCell.Tag = "corner";
                cornerCell.Margin = System.Windows.Forms.Padding.Empty;
                ValuesArea.Controls.Add(cornerCell, 0, 0);
                ValuesArea.SetColumnSpan(cornerCell, CornerWidth);
                ValuesArea.SetRowSpan(cornerCell, CornerHeight);
            }
            //заполняем по измерениям
            for (int i = 0; i < c; i++)
            {
                INamedHierarchy<INamed> item = h.GetItemByPosition(i) as INamedHierarchy<INamed>;
                if (item.Tag.Equals(RapidAnalysisDimensionPosition.Columns))
                {
                    var ch = item.GetItemByPosition(0) as INamedHierarchy<INamed>;
                    FillFrameRecursively(ch, CornerWidth, 0, ch.DeepestLevel, true, ch.Tag as IDimension);
                }
                if (item.Tag.Equals(RapidAnalysisDimensionPosition.Rows))
                {
                    var ch = item.GetItemByPosition(0) as INamedHierarchy<INamed>;
                    FillFrameRecursively(ch, 0, CornerHeight, ch.DeepestLevel, false, ch.Tag as IDimension);
                }
            }
            //строим ячейки данных
            for (int i = CornerHeight; i < RowsCount; i++)
            {
                for (int j = CornerWidth; j < ColumnsCount; j++)
                {
                    TextBox dataCell = new TextBox();
                    dataCell.TextAlign = HorizontalAlignment.Right;
                    dataCell.BorderStyle = BorderStyle.FixedSingle;
                    dataCell.Text = "";                    
                    dataCell.Dock = DockStyle.Fill;
                    dataCell.Validating += new CancelEventHandler(dataCell_Validating);
                    dataCell.KeyDown+=new KeyEventHandler(dataCell_KeyDown);
                    dataCell.Margin = System.Windows.Forms.Padding.Empty;
                    ValuesArea.Controls.Add(dataCell, j, i);
                }
            }
        }

        /// <summary>
        /// Возвращает фиксированные элементы измерений
        /// </summary>
        private List<DimensionItem> FixedDimensionItems
        {
            get
            {
                List<DimensionItem> fixedDimItems = GetAllCheckedDimensionItems(DimensionItemsList.Nodes[2].Nodes);
                return fixedDimItems;
            }
        }        

        /// <summary>
        /// Возвращает список элементов измерений, ассоциированных с ячейкой
        /// </summary>        
        /// <returns></returns>
        /// <remarks>без учета фиксированных элементов измерений</remarks>
        private List<DimensionItem> GetDimensionItemsOfDataCell(int rowIndex, int columnIndex)
        {
            List<DimensionItem> dimitems = new List<DimensionItem>();
            //проходим все заголовочные ячейки
            for (int jj = 0; jj < 2; jj++)
            {
                int jk = 0;
                int ik = rowIndex;
                if (jj == 1)
                {
                    jk = columnIndex;
                    ik = 0;
                }
                RapidAnalysisCaptionCell cap = ValuesArea.GetControlFromPosition(jk, ik).Tag as RapidAnalysisCaptionCell;
                DimensionItem dimitem = null;
                while (cap != null)
                {
                    dimitem = cap.DimensionItem;
                    if (jj == 1)
                    {
                        ik++;
                    }
                    else
                    {
                        jk++;
                    }
                    cap = ValuesArea.GetControlFromPosition(jk, ik).Tag as RapidAnalysisCaptionCell;
                }
                if (dimitem != null)
                {
                    dimitems.Add(dimitem);
                }
            }            
            return dimitems;
        }

        /// <summary>
        /// Обновляет список фактов в соответствии с выбранными элементами измерений в левой части быстрого анализа
        /// </summary>
        private void UpdateValues()
        {
            ValuesProgress.Visible = true;
            ValuesArea.SuspendLayout();
            TreeNodeCollection nodes = DimensionItemsList.Nodes;            
            //строим рамку
            UpdateValuesFrames(nodes);
            //указываем названия осей на графике и выполняем
            //прочие подготовительные действия для работы с
            //графиком
            GraphPane myPane = RapidAnalysisGraph.GraphPane;
            IDimension dim = _curRapidAnalysisInfo.ColumnsDimension;
            myPane.XAxis.Title.Text = dim.Name;
            dim = _curRapidAnalysisInfo.RowsDimension;
            myPane.YAxis.Title.Text = Resources.BI_RAPIDANALYSIS_VALUES_CAPTION;
            myPane.CurveList.Clear();    
            List<PointPairList> graphLines = new List<PointPairList>();
            List<string> graphLinesNames = new List<string>();
            List<string> colDimensionItemNames = new List<string>();
            PointPairList graphPoints = null;            
            //получаем фиксированные элементы измерений
            List<DimensionItem> fixedDimItems = FixedDimensionItems;
            //получаем ключи фиксированных элементов измерений, одновременно вычленяя мир (если он там)
            int wKey = -1;
            string wdimlabel = ProjectLink.RepositoryLink.WorldsDimension.Label;
            List<DimensionItem> fixeddimitems = new List<DimensionItem>();
            for (int i = 0; i < fixedDimItems.Count; i++)
            {
                DimensionItem curdimitem = fixedDimItems[i];
                if (curdimitem.DimensionLabel == wdimlabel)
                {
                    wKey = (int)(curdimitem.Key);
                }
                else
                {
                    fixeddimitems.Add(curdimitem);
                }
            }            
            //заполняем сами значения
            //bbb _valueableRows = false;
            _firstValueableY = -1;
            _firstValueableX = -1;                
            for (int i = 0; i < ValuesArea.RowCount; i++)
            {
                bool toStartNewGraphLine = true;
                if ((graphPoints != null) && (graphPoints.Count > 0))
                {
                    graphLines.Add(graphPoints);
                }
                graphPoints = new PointPairList();                
                for (int j = 0; j < ValuesArea.ColumnCount; j++)
                {
                    ValuesProgress.Refresh();
                    Control cell = ValuesArea.GetControlFromPosition(j, i) as Control;
                    if (cell.Tag == null)
                    {                 
                        //значит это ячейка с датой
                        if (_firstValueableX == -1)
                        {
                            _firstValueableX = j;
                            //bbb _valueableRows = true;
                        }
                        if (_firstValueableY == -1)
                        {
                            _firstValueableY = i;
                        }
                        List<DimensionItem> dimensionitems = GetDimensionItemsOfDataCell(i, j);                        
                        string columnCaption = "";                        
                        string graphLineCaption = "";
                        foreach(DimensionItem dimitem in dimensionitems)
                        {                            
                            if (toStartNewGraphLine&&(_curRapidAnalysisInfo.RowsDimension.Label==dimitem.DimensionLabel))                             
                            {
                                graphLineCaption += "|" + _curRapidAnalysisInfo.RowsDimension.Items.GetFirstItemByTag(dimitem.Key).Name;
                            }
                            else
                            {
                                if (_curRapidAnalysisInfo.ColumnsDimension.Label == dimitem.DimensionLabel)                                
                                {
                                    columnCaption = _curRapidAnalysisInfo.ColumnsDimension.Items.GetFirstItemByTag(dimitem.Key).Name;
                                }
                            }
                        }
                        if (toStartNewGraphLine)
                        {
                            toStartNewGraphLine = false;                            
                            graphLineCaption = graphLineCaption.Substring(1);                                                    
                            graphLinesNames.Add(graphLineCaption);
                        }
                        //получаем ключи элементов измерений, одновременно вычленяя мир                        
                        List<DimensionItem> dimitems = new List<DimensionItem>();
                        foreach(DimensionItem curdimitem in dimensionitems)
                        {                            
                            if (curdimitem.DimensionLabel == wdimlabel)
                            {
                                wKey = (int)(curdimitem.Key);
                            }
                            else
                            {
                                dimitems.Add(curdimitem);
                            }
                        }
                        //добавляем в элементы измерений прочитанные ранее фиксированные
                        dimitems.AddRange(fixeddimitems);
                        //вычленяем дату (если она есть)                                                
                        int c = dimitems.Count;
                        DateTime dt = Constants.NullDate;
                        for (int k = 0; k < c; k++)
                        {
                            DimensionItem di = dimitems[k];
                            if (di.Key is DateTime)
                            {
                                dt = (DateTime)(di.Key);
                                dimitems.Remove(di);
                                break;
                            }
                        }
                        //получаем значение на эти элементы измерений                                       
                        double val = -1;
                        if (dimitems.Count > 0)
                        {
                            val = (_curViewingObject as ICube).ReadValue(dimitems, dt, wKey);
                        }
                        else
                        {
                            val = _curViewingObject.ReadValue(dt, wKey);
                        }
                        if (val != Constants.NullDouble)
                        {                            
                            cell.Text = val.ToString(CultureInfo.CurrentCulture);
                        }
                        graphPoints.Add(0, val);
                        while (colDimensionItemNames.Count <= j - _firstValueableX)
                        {
                            colDimensionItemNames.Add("");
                        }
                        colDimensionItemNames[j - _firstValueableX] = columnCaption;
                    }
                }
            }
            if ((graphPoints != null) && (graphPoints.Count > 0))
            {
                graphLines.Add(graphPoints);
            }
            //заполняем график                        
            myPane.XAxis.Scale.TextLabels = colDimensionItemNames.ToArray();            
            int cc = graphLines.Count;
            for (int i = 0; i < cc; i++)
            {
                LineItem myCurve = myPane.AddCurve(graphLinesNames[i],
                    graphLines[i], GeneralMethods.GetColorByIndex(i), (SymbolType)GeneralMethods.GetGraphSymbolByIndex(i));
                myCurve.Symbol.Fill = new Fill(Color.White);
            }
            RapidAnalysisGraph.AxisChange();
            RapidAnalysisGraph.Invalidate();
            //восстанавливаем отрисовку
            ValuesArea.ResumeLayout();
            ValuesProgress.Visible = false;
        }        

        /// <summary>
        /// Обновляет список элементов измерений в соответствии с ядром быстрого анализа
        /// </summary>
        private void UpdateDimensionItemsList()
        {
            DimensionItemsList.BeginUpdate();
            TreeNodeCollection nodes = DimensionItemsList.Nodes;
            nodes.Clear();            
            for (int posindex = 1; posindex < 4; posindex++)
            {
                RapidAnalysisDimensionPosition pos = (RapidAnalysisDimensionPosition)posindex;
                List<IDimension> Dimensions = new List<IDimension>();                
                string captionString = "";
                switch (pos)
                {
                    case RapidAnalysisDimensionPosition.Columns:
                        Dimensions.Add(_curRapidAnalysisInfo.ColumnsDimension);
                        captionString = Resources.BI_RAPIDANALYSIS_DIMENSION_ITEMS_HORIZONTAL;
                        break;
                    case RapidAnalysisDimensionPosition.Rows:
                        Dimensions.Add(_curRapidAnalysisInfo.RowsDimension);
                        captionString = Resources.BI_RAPIDANALYSIS_DIMENSION_ITEMS_VERTICAL;
                        break;
                    case RapidAnalysisDimensionPosition.Fixed:
                        Dimensions = _curRapidAnalysisInfo.FixedDimensions;
                        captionString = Resources.BI_RAPIDANALYSIS_DIMENSION_ITEMS_FIXED;
                        break;
                }                               
                int c = Dimensions.Count;
                TreeNode[] dimensionsNodes = new TreeNode[c];
                for (int i = 0; i < c; i++)
                {
                    bool noCheckedFixDimItems = true;
                    IDimension Dimension = Dimensions[i];
                    int cc = Dimension.Items.ItemsCount;
                    TreeNode[] dimensionitemsNodes = new TreeNode[cc];
                    TreeNode dimensionNode = null;                    
                    for (int j = 0; j < cc; j++)
                    {
                        INamedHierarchy<INamed> DimensionItem = Dimension.Items.GetItemByPosition(j) as INamedHierarchy<INamed>;
                        TreeNode dimensionitemNode = new TreeNode(DimensionItem.Name, GetChildNodesRecursively(DimensionItem));
                        dimensionitemNode.Tag = DimensionItem.Tag;
                        if ((noCheckedFixDimItems) && (pos == RapidAnalysisDimensionPosition.Fixed))                            
                        {
                            dimensionitemNode.Checked = true;
                            noCheckedFixDimItems = false;
                        }
                        dimensionitemsNodes[j] = dimensionitemNode;
                    }
                    dimensionNode = new TreeNode(Dimension.Name, dimensionitemsNodes);                    
                    dimensionNode.Tag = Dimension;
                    dimensionNode.ForeColor = SystemColors.HotTrack;
                    dimensionsNodes[i] = dimensionNode;
                }
                TreeNode posNode = new TreeNode(captionString, dimensionsNodes);
                posNode.ForeColor = SystemColors.GrayText;
                posNode.Tag = pos;
                nodes.Add(posNode);                
            }            
            DimensionItemsList.ExpandAll();
            DimensionItemsList.EndUpdate();
        }
        
        /// <summary>
        /// Актуализирует отметки измерений и их элементов
        /// </summary>        
        /// <remarks>если снята/выставлена отметка у измерения, то
        /// снята/выставлена и со всех его элементов; если выставлена
        /// отметка для элемента, то выставлена отметка и для его
        /// измерения; если сняты отметки со всех элементов, то
        /// снята отметка и с их измерения</remarks>
        private void ActualizeCheckStatesForNode(TreeNode topNode, bool isRecursive)
        {
            if ((!_programCheckBoxesUpdateFlag) && (topNode.Level > 0))            
            {
                TreeNode dimnode = GetDimensionNodeByChildNode(topNode);
                if (dimnode.Parent.Index == 2)
                {
                    //фиксированное измерение
                    List<DimensionItem> nulldimitems = new List<DimensionItem>();
                    SetAllCheckedDimensionItems(dimnode.Nodes, nulldimitems, new List<DimensionItem>());
                    _programCheckBoxesUpdateFlag = true;
                    topNode.Checked = true;
                    _programCheckBoxesUpdateFlag = false;                    
                }
                else
                {
                    TreeNodeCollection nodes = topNode.Nodes;
                    foreach(TreeNode node in nodes)
                    {                        
                        _programCheckBoxesUpdateFlag = true;
                        node.Checked = topNode.Checked;
                        _programCheckBoxesUpdateFlag = false;
                        ActualizeCheckStatesForNode(node, true);
                    }
                    if (!isRecursive)
                    {
                        TreeNode parentNode = topNode.Parent;
                        while ((parentNode != null) && (parentNode.Level >= 1))
                        {                            
                            bool haveChecked = false;
                            foreach(TreeNode node in parentNode.Nodes)                            
                            {
                                if (haveChecked)
                                    break;                                
                                haveChecked = node.Checked;
                            }
                            _programCheckBoxesUpdateFlag = true;
                            parentNode.Checked = haveChecked;
                            _programCheckBoxesUpdateFlag = false;
                            parentNode = parentNode.Parent;
                        }
                    }
                }
                if (!isRecursive)
                {
                    UpdateValues();
                }
            }
        }

        /// <summary>
        /// Возвращает узел измерения для указанного дочернего узла
        /// </summary>
        /// <param name="selectedNode"></param>
        /// <returns></returns>
        private static TreeNode GetDimensionNodeByChildNode(TreeNode selectedNode)
        {
            TreeNode dimNode = selectedNode;
            while (dimNode.Level > 1)
            {
                dimNode = dimNode.Parent;
            }
            if (dimNode.Level == 0)
            {
                return null;
            }
            return dimNode;
        }

        /// <summary>
        /// Возвращает узел выделенного измерения
        /// </summary>
        private TreeNode SelectedDimensionNode
        {
            get
            {
                return GetDimensionNodeByChildNode(DimensionItemsList.SelectedNode);
            }
        }

        /// <summary>
        /// Возвращает выделенное измерение
        /// </summary>
        private IDimension SelectedDimension
        {
            get
            {
                TreeNode dimNode = SelectedDimensionNode;
                IDimension curDimension = null;
                if (dimNode != null)
                {
                    List<IDimension> curPositionDimensions = new List<IDimension>();
                    switch ((RapidAnalysisDimensionPosition)dimNode.Parent.Index)
                    {
                        case RapidAnalysisDimensionPosition.Columns:
                            curPositionDimensions.Add(_curRapidAnalysisInfo.ColumnsDimension);
                            break;
                        case RapidAnalysisDimensionPosition.Rows:
                            curPositionDimensions.Add(_curRapidAnalysisInfo.RowsDimension);
                            break;
                        case RapidAnalysisDimensionPosition.Fixed:
                            curPositionDimensions = _curRapidAnalysisInfo.FixedDimensions;
                            break;
                    }
                    curDimension = curPositionDimensions[dimNode.Index];
                }
                return curDimension;
            }
        }

        /// <summary>
        /// Обновляет доступность кнопок тулбара измерений
        /// </summary>                
        private void UpdateDimensionsToolBar()
        {                        
            TreeNode dimNode = SelectedDimensionNode;
            bool isSelectedDimOrInd = dimNode != null;
            bool isNotTop = isSelectedDimOrInd && ((dimNode.Parent.Index > 0) || (dimNode.Index > 0));
            bool isNotDown = isSelectedDimOrInd && ((dimNode.Parent.Index != 2) && ((dimNode.Parent.NextNode.Nodes.Count > 0)||(dimNode.Parent.Nodes.Count>1)) || (dimNode.Index < (dimNode.Parent.Nodes.Count - 1)));            
            UpDimensionButton.Enabled = isNotTop;
            DownDimensionButton.Enabled = isNotDown;
        }

        private void SelectTreeNodeByTag(TreeView tree, object tag, TreeNode root)
        {
            TreeNodeCollection treenodes = null;
            if (root == null)
            {
                treenodes = tree.Nodes;
            }
            else
            {
                treenodes = root.Nodes;
            }
            int c = treenodes.Count;
            for (int i = 0; i < c; i++)
            {
                SelectTreeNodeByTag(tree, tag, treenodes[i]);
            }
            if ((root != null) && (tag.Equals(root.Tag)))            
            {
                tree.SelectedNode = root;
            }
        }

        /// <summary>
        /// Отмечает заново все отмеченные ранее элементы измерений
        /// </summary>
        /// <param name="nodes"></param>        
        private void SetAllCheckedDimensionItems(TreeNodeCollection nodes, List<DimensionItem> dimensionItems, List<DimensionItem> fixedDimensionItems)
        {        
            int c = nodes.Count;            
            for (int i = 0; i < c; i++)
            {
                TreeNode node = nodes[i];                
                SetAllCheckedDimensionItems(node.Nodes, dimensionItems, fixedDimensionItems);
                object nodeTag = node.Tag;
                if ((nodeTag is int) || (nodeTag is DateTime) || (nodeTag is DatePeriod))
                {
                    IDimension dim = GetDimensionNodeByChildNode(node).Tag as IDimension;
                    bool toCheck = false;
                    for (int j = 0; j < dimensionItems.Count; j++)
                    {
                        if ((dimensionItems[j].DimensionLabel == dim.Label) && (dimensionItems[j].Key == nodeTag))
                        {
                            toCheck = true;
                            break;
                        }
                    }
                    bool fixedcontains = false;
                    for (int j = 0; j < fixedDimensionItems.Count; j++)
                    {
                        if ((fixedDimensionItems[j].DimensionLabel == dim.Label) && (fixedDimensionItems[j].Key == nodeTag))
                        {
                            fixedcontains = true;
                            break;
                        }
                    }
                    if ((toCheck && fixedcontains && _noFixedInds) || (!fixedcontains))
                    {
                        if (toCheck && fixedcontains)
                        {
                            _noFixedInds = false;
                        }
                        _programCheckBoxesUpdateFlag = true;
                        node.Checked = toCheck;
                        _programCheckBoxesUpdateFlag = false;
                    }
                }
            }
        }

        /// <summary>
        /// Возвращает список всех отмеченных элементов измерений
        /// </summary>
        /// <param name="nodes"></param>
        /// <returns>ключи элементов</returns>
        private List<DimensionItem> GetAllCheckedDimensionItems(TreeNodeCollection nodes)
        {
            List<DimensionItem> dimitems = new List<DimensionItem>();
            int c = nodes.Count;
            for (int i = 0; i < c; i++)
            {
                TreeNode node = nodes[i];
                dimitems.AddRange(GetAllCheckedDimensionItems(node.Nodes));
                if (node.Checked)
                {
                    object nodeTag = node.Tag;
                    if ((nodeTag is int) || (nodeTag is DateTime) || (nodeTag is DatePeriod))                    
                    {
                        dimitems.Add(new DimensionItem((GetDimensionNodeByChildNode(node).Tag as IDimension).Label, nodeTag));
                    }
                }
            }
            return dimitems;
        }

        /// <summary>
        /// Опускает измерение ниже
        /// </summary>
        private void DownDimension()
        {            
            object selectedBefore = DimensionItemsList.SelectedNode.Tag;
            List<DimensionItem> checkedDimensionItems = GetAllCheckedDimensionItems(DimensionItemsList.Nodes);
            _curRapidAnalysisInfo.MoveDimensionDown(SelectedDimension);
            UpdateDimensionItemsList();
            _noFixedInds = true;
            List<DimensionItem> checkedFixedDimensionItems = new List<DimensionItem>();
            for (int i = 0; i < _curRapidAnalysisInfo.FixedDimensions.Count; i++)
            {
                IDimension dim = _curRapidAnalysisInfo.FixedDimensions[i];
                List<Guid> items = dim.Items.Ids;
                int c = items.Count;
                for (int j = 0; j < c; j++)
                {
                    object nodeTag = dim.Items.GetItemById(items[j], true).Tag;
                    checkedDimensionItems.Add(new DimensionItem(dim.Label, nodeTag));
                }
            }
            SetAllCheckedDimensionItems(DimensionItemsList.Nodes, checkedDimensionItems, checkedFixedDimensionItems);
            SelectTreeNodeByTag(DimensionItemsList, selectedBefore, null);            
        }

        /// <summary>
        /// Поднимает измерение выше
        /// </summary>
        private void UpDimension()
        {
            object selectedBefore = DimensionItemsList.SelectedNode.Tag;
            List<DimensionItem> checkedDimensionItems = GetAllCheckedDimensionItems(DimensionItemsList.Nodes);
            _curRapidAnalysisInfo.MoveDimensionUp(SelectedDimension);
            UpdateDimensionItemsList();
            _noFixedInds = true;
            List<DimensionItem> checkedFixedDimensionItems = new List<DimensionItem>();
            for (int i = 0; i < _curRapidAnalysisInfo.FixedDimensions.Count; i++)
            {
                IDimension dim = _curRapidAnalysisInfo.FixedDimensions[i];                
                List<Guid> items = dim.Items.Ids;
                int c = items.Count;
                for (int j = 0; j < c; j++)
                {
                    object nodeTag = dim.Items.GetItemById(items[j], true).Tag;
                    checkedDimensionItems.Add(new DimensionItem(dim.Label, nodeTag));
                }
            }
            SetAllCheckedDimensionItems(DimensionItemsList.Nodes, checkedDimensionItems, checkedFixedDimensionItems);
            SelectTreeNodeByTag(DimensionItemsList, selectedBefore, null);            
        }

        /// <summary>
        /// Устанавливает новое значение для факта
        /// </summary>
        /// <param name="row"></param>
        /// <param name="column"></param>
        /// <returns>успех?</returns>
        private bool UpdateFactValue(int row, int column, bool toUpdateGraph)
        {            
            List<DimensionItem> dimitems = GetDimensionItemsOfDataCell(row, column);
            dimitems.AddRange(FixedDimensionItems);
            //**вычленяем мир
            string wdimlabel = ProjectLink.RepositoryLink.WorldsDimension.Label;
            int wkey = -1;
            for (int i = 0; i < dimitems.Count; i++)
            {
                if (dimitems[i].DimensionLabel == wdimlabel)                
                {
                    wkey = (int)(dimitems[i].Key);
                    dimitems.RemoveAt(i);
                    break;
                }
            }
            //**конец вычленения мира
            
            //вычленяем дату (если она есть)                                                
            int c = dimitems.Count;
            DateTime dt = Constants.NullDate;
            for (int k = 0; k < c; k++)
            {
                DimensionItem di = dimitems[k];
                if (di.Key is DateTime)
                {
                    dt = (DateTime)(di.Key);
                    dimitems.Remove(di);
                    break;
                }
            }
            //пишем
            TextBox cell = ValuesArea.GetControlFromPosition(column, row) as TextBox;
            cell.TextAlign = HorizontalAlignment.Right;
            object val = cell.Text.Trim();      
            try
            {
                if (dimitems.Count > 0)
                {
                    val = (_curViewingObject as ICube).WriteValue(dimitems, dt, wkey, val);
                }
                else
                {
                    val = _curViewingObject.WriteValue(dt, wkey, val);
                }
                if (toUpdateGraph)
                {                    
                    CurveItem cur = RapidAnalysisGraph.GraphPane.CurveList[row - _firstValueableY];
                    PointPair pair = cur.Points[column - _firstValueableX];
                    pair.Y = (double)val;
                    RapidAnalysisGraph.AxisChange();
                    RapidAnalysisGraph.Invalidate();                    
                }
            }
            catch (BIFormatNumericException ex)
            {
                GeneralMethods.ShowUserError(ex.Message);
                return false;
            }
            return true;
        }        

        /// <summary>
        /// Подготавливает график
        /// </summary>
        private void PrepareGraph()
        {                        
            GraphPane myPane = RapidAnalysisGraph.GraphPane;            
            myPane.IsIgnoreMissing = true;
            myPane.Title.Text = _curViewingObject.Name;
            myPane.XAxis.Title.Text = string.Empty;
            myPane.YAxis.Title.Text = string.Empty;

            // Show the x axis grid
            myPane.XAxis.MajorGrid.IsVisible = true;

            // Make the Y axis scale red
            myPane.YAxis.Scale.FontSpec.FontColor = Color.Red;
            myPane.YAxis.Title.FontSpec.FontColor = Color.Red;
            // Align the Y axis labels so they are flush to the axis
            myPane.YAxis.Scale.Align = AlignP.Inside;
            myPane.XAxis.Type = AxisType.Text;
            myPane.XAxis.Scale.Min = 0;
            myPane.XAxis.Scale.MinorStep = 1;
            myPane.XAxis.Scale.MajorStep = 1;            

            // Fill the axis background with a gradient
            myPane.Chart.Fill = new Fill(Color.White, Color.LightGray, 45.0f);

            /*// Add a text box with instructions
            TextObj text = new TextObj(
                //в ресурс следующую строку нельзя вытащить, потому что в этом случае почему-то не обрабатываются переносы строк \n
                "Масштабирование: зажать правую кнопку и тащить",
                0.05f, 0.95f, CoordType.ChartFraction, AlignH.Left, AlignV.Bottom);
            text.FontSpec.StringAlignment = StringAlignment.Near;
            myPane.GraphObjList.Add(text);*/
                        
            // Enable scrollbars if needed
            RapidAnalysisGraph.IsShowHScrollBar = true;
            RapidAnalysisGraph.IsShowVScrollBar = true;
            RapidAnalysisGraph.IsAutoScrollRange = true;            

            // OPTIONAL: Show tooltips when the mouse hovers over a point
            RapidAnalysisGraph.IsShowPointValues = true;

            // Make sure the Graph gets redrawn
            RapidAnalysisGraph.Invalidate();
        }

        /*/// <summary>
        /// Отображает диалог моделирования
        /// </summary>
        private void ShowModellingDialog()
        {
            if (!_valueableRows)
            {
                MessageBox.Show(Resources.RS_ERROR_ANALYSIS_REQUIRE_VALUEABLE_ROWS, Resources.RS_ERROR_USER_CAPTION, MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }
            IDimension columnsdim = _curRapidAnalysisInfo.columnsDimension;
            if (columnsdim.dimensionKind == DimensionKind.Calendar)
            {
                ModellingDialog anal = new ModellingDialog(AnalysisMethods.Trend, _curViewingObject.Name, columnsdim.Name, ValuesArea, (columnsdim as ICalendarDimension).dynamics);
                anal.ShowDialog();
                return;
            }            
            MessageBox.Show(Resources.RS_ERROR_ANALYSIS_CALENDAR_DIMENSIONS_SHOULD_BE_AT_COLUMNS, Resources.RS_ERROR_USER_CAPTION, MessageBoxButtons.OK, MessageBoxIcon.Error);
        }*/

        /// <summary>
        /// Инициализирует быстрый анализ
        /// </summary>
        private void Initialize()
        {
            this.SetObjectViewerCaption(this.Text, _curViewingObject);
            _programValidatedFlag = false;
            _closing = false;
            _curRapidAnalysisInfo = new RapidAnalysisInfo(ProjectLink, _curViewingObject);            
            ValuesAreaPanel.Height = 400;
            PrepareGraph();
            UpdateDimensionItemsList();
            UpdateValues();
        }

        public RapidAnalysisForm(ProjectExplorerForm projectExplorerForm, IDateAndFactCorrelations viewingObject)
        {
            _projectExplorerForm = projectExplorerForm;
            _programCheckBoxesUpdateFlag = false;
            _curViewingObject = viewingObject;
            InitializeComponent();
        }

        private void RapidAnalysis_Load(object sender, EventArgs e)
        {            
            Initialize();            
        }

        private void DimensionItemsList_AfterCheck(object sender, TreeViewEventArgs e)
        {                        
            ActualizeCheckStatesForNode(e.Node, false);         
        }

        private void DimensionItemsList_AfterSelect(object sender, TreeViewEventArgs e)
        {
            UpdateDimensionsToolBar();
        }

        private void DimensionItemsList_BeforeCheck(object sender, TreeViewCancelEventArgs e)
        {
            if ((e.Node.Level == 0) || (e.Node.Level == 1) && (e.Node.Parent.Index == 2))            
            {
                e.Cancel = true;
            }
        }

        private void DownDimensionButton_Click(object sender, EventArgs e)
        {
            DownDimension();
        }

        private void UpDimensionButton_Click(object sender, EventArgs e)
        {
            UpDimension();
        }

        private void dataCell_Validating(object sender, CancelEventArgs e)
        {
            if ((!_closing) && (!_programValidatedFlag))            
            {
                TableLayoutPanelCellPosition pos = ValuesArea.GetCellPosition(sender as Control);
                e.Cancel = !UpdateFactValue(pos.Row, pos.Column, true);
            }
        }

        private void dataCell_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Enter)
            {
                TableLayoutPanelCellPosition pos = ValuesArea.GetCellPosition(sender as Control);
                if (!UpdateFactValue(pos.Row, pos.Column, true))
                {
                    (sender as TextBox).SelectAll();
                }
                else
                {
                    _programValidatedFlag = true;
                    ValuesArea.Focus();
                    _programValidatedFlag = false;
                }
            }
        }
        
        private void RapidAnalysis_FormClosed(object sender, FormClosedEventArgs e)
        {
            _projectExplorerForm.RapidAnalysisWindows.Remove(this);            
        }

        private void трендToolStripMenuItem_Click(object sender, EventArgs e)
        {
            //ShowModellingDialog();
        }

        private string RapidAnalysisGraph_PointEditEvent(ZedGraphControl sender, GraphPane pane, CurveItem curve, int iPt)
        {
            PointPair pt = curve[iPt];
            double val = pt.Y;
            int row = pane.CurveList.IndexOf(curve) + _firstValueableY;
            int col = iPt + _firstValueableX;
            string strval = val.ToString(CultureInfo.CurrentCulture);
            (ValuesArea.GetControlFromPosition(col, row) as TextBox).Text = strval;
            UpdateFactValue(row, col, false);
            RapidAnalysisGraph.AxisChange();
            RapidAnalysisGraph.Invalidate();
            return strval;
        }

        private void RapidAnalysisForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            _closing = true;
        }

        private void оПрограммеToolStripMenuItem_Click(object sender, EventArgs e)
        {
            _projectExplorerForm.ShowAboutDialog();
        }

        private void закрытьToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Close();
        }

        private void перейтиКОбозревателюМетаданныхToolStripMenuItem_Click(object sender, EventArgs e)
        {
            _projectExplorerForm.Focus();            
        }
    }
}
